#483 [frontend] separate manage.py commands to their own files
Merged 5 years ago by msuchy. Opened 5 years ago by frostyx.
copr/ frostyx/copr separate-frontend-commands  into  master

empty or binary file added
@@ -0,0 +1,34 @@ 

+ from flask_script import Command, Option

+ from coprs import db

+ from coprs import models

+ from coprs.views.misc import create_user_wrapper

+ 

+ 

+ class AddUserCommand(Command):

+ 

+     """

+     You should not use regularly as that user will not be related to FAS account.

+     This should be used only for testing or adding special accounts e.g. proxy user.

+     """

+ 

+     def run(self, name, mail, **kwargs):

+         user = models.User.query.filter(models.User.username == name).first()

+         if user:

+             print("User named {0} already exists.".format(name))

+             return

+ 

+         user = create_user_wrapper(name, mail)

+         if kwargs["api_token"]:

+             user.api_token = kwargs["api_token"]

+         if kwargs["api_login"]:

+             user.api_token = kwargs["api_login"]

+ 

+         db.session.add(user)

+         db.session.commit()

+ 

+     option_list = (

+         Option("name"),

+         Option("mail"),

+         Option("--api_token", default=None, required=False),

+         Option("--api_login", default=None, required=False),

+     )

@@ -0,0 +1,43 @@ 

+ import datetime

+ from flask_script import Option

+ from coprs import db

+ from coprs import app

+ from coprs import exceptions

+ from coprs.logic import coprs_logic

+ from commands.create_chroot import ChrootCommand

+ 

+ 

+ class AlterChrootCommand(ChrootCommand):

+ 

+     "Activates or deactivates a chroot"

+ 

+     def run(self, chroot_names, action):

+         activate = (action == "activate")

+         for chroot_name in chroot_names:

+             try:

+                 mock_chroot = coprs_logic.MockChrootsLogic.edit_by_name(

+                     chroot_name, activate)

+ 

+                 if action != "eol":

+                     continue

+ 

+                 for copr_chroot in mock_chroot.copr_chroots:

+                     delete_after_days = app.config["DELETE_EOL_CHROOTS_AFTER"] + 1

+                     delete_after_timestamp = datetime.datetime.now() + datetime.timedelta(delete_after_days)

+                     # Workarounding an auth here

+                     coprs_logic.CoprChrootsLogic.update_chroot(copr_chroot.copr.user, copr_chroot,

+                                                                delete_after=delete_after_timestamp)

+                 db.session.commit()

+             except exceptions.MalformedArgumentException:

+                 self.print_invalid_format(chroot_name)

+             except exceptions.NotFoundException:

+                 self.print_doesnt_exist(chroot_name)

+ 

+     option_list = ChrootCommand.option_list + (

+         Option("--action",

+                "-a",

+                dest="action",

+                help="Action to take - currently activate or deactivate",

+                choices=["activate", "deactivate", "eol"],

+                required=True),

+     )

@@ -0,0 +1,54 @@ 

+ from coprs import db

+ from coprs import models

+ from flask_script import Command, Option, Group

+ 

+ 

+ class AlterUserCommand(Command):

+ 

+     def run(self, name, **kwargs):

+         user = models.User.query.filter(

+             models.User.username == name).first()

+         if not user:

+             print("No user named {0}.".format(name))

+             return

+ 

+         if kwargs["admin"]:

+             user.admin = True

+         if kwargs["no_admin"]:

+             user.admin = False

+         if kwargs["proven"]:

+             user.proven = True

+         if kwargs["no_proven"]:

+             user.proven = False

+         if kwargs["proxy"]:

+             user.proxy = True

+         if kwargs["no_proxy"]:

+             user.proxy = False

+ 

+         db.session.add(user)

+         db.session.commit()

+ 

+     option_list = (

+         Option("name"),

+         Group(

+             Option("--admin",

+                    action="store_true"),

+             Option("--no-admin",

+                    action="store_true"),

+             exclusive=True

+         ),

+         Group(

+             Option("--proven",

+                    action="store_true"),

+             Option("--no-proven",

+                    action="store_true"),

+             exclusive=True

+         ),

+         Group(

+             Option("--proxy",

+                    action="store_true"),

+             Option("--no-proxy",

+                    action="store_true"),

+             exclusive=True

+         )

+     )

@@ -0,0 +1,31 @@ 

+ from coprs import db

+ from coprs.logic import coprs_logic, packages_logic, actions_logic

+ from commands.rawhide_to_release import RawhideToReleaseCommand

+ 

+ 

+ class BackendRawhideToReleaseCommand(RawhideToReleaseCommand):

+ 

+     "Copy backend data of the latest successful rawhide builds into a new chroot"

+ 

+     def run(self, rawhide_chroot, dest_chroot):

+         for copr in coprs_logic.CoprsLogic.get_all():

+             if not self.has_rawhide(copr):

+                 continue

+ 

+             data = {"copr": copr.name,

+                     "user": copr.owner_name,

+                     "rawhide_chroot": rawhide_chroot,

+                     "dest_chroot": dest_chroot,

+                     "builds": []}

+ 

+             for package in packages_logic.PackagesLogic.get_all(copr.id):

+                 last_build = package.last_build(successful=True)

+                 if last_build:

+                     data["builds"].append(last_build.result_dir)

+ 

+             if len(data["builds"]):

+                 actions_logic.ActionsLogic.send_rawhide_to_release(data)

+                 print("Created copy action from {}/{} to {}/{}"

+                       .format(copr.full_name, rawhide_chroot, copr.full_name, dest_chroot))

+ 

+         db.session.commit()

@@ -0,0 +1,51 @@ 

+ from flask_script import Command, Option

+ from coprs import exceptions

+ from coprs import db

+ from coprs.helpers import chroot_to_branch

+ from coprs.logic import coprs_logic

+ 

+ 

+ class ChrootCommand(Command):

+ 

+     def print_invalid_format(self, chroot_name):

+         print(

+             "{0} - invalid chroot format, must be '{release}-{version}-{arch}'."

+                 .format(chroot_name))

+ 

+     def print_already_exists(self, chroot_name):

+         print("{0} - already exists.".format(chroot_name))

+ 

+     def print_doesnt_exist(self, chroot_name):

+         print("{0} - chroot doesn\"t exist.".format(chroot_name))

+ 

+     option_list = (

+         Option("chroot_names",

+                help="Chroot name, e.g. fedora-18-x86_64.",

+                nargs="+"),

+     )

+ 

+ 

+ class CreateChrootCommand(ChrootCommand):

+ 

+     "Creates a mock chroot in DB"

+ 

+     def __init__(self):

+         self.option_list += Option(

+             "--dist-git-branch",

+             "-b",

+             dest="branch",

+             help="Branch name for this set of new chroots"),

+ 

+     def run(self, chroot_names, branch=None):

+         for chroot_name in chroot_names:

+             if not branch:

+                 branch = chroot_to_branch(chroot_name)

+             branch_object = coprs_logic.BranchesLogic.get_or_create(branch)

+             try:

+                 chroot = coprs_logic.MockChrootsLogic.add(chroot_name)

+                 chroot.distgit_branch = branch_object

+                 db.session.commit()

+             except exceptions.MalformedArgumentException:

+                 self.print_invalid_format(chroot_name)

+             except exceptions.DuplicateException:

+                 self.print_already_exists(chroot_name)

@@ -0,0 +1,34 @@ 

+ from flask_script import Command, Option

+ from coprs import db

+ from coprs.logic import builds_logic

+ from commands.create_sqlite_file import CreateSqliteFileCommand

+ 

+ 

+ class CreateDBCommand(Command):

+ 

+     """

+     Create the DB schema

+     """

+ 

+     def run(self, alembic_ini=None):

+         CreateSqliteFileCommand().run()

+         db.create_all()

+ 

+         # load the Alembic configuration and generate the

+         # version table, "stamping" it with the most recent rev:

+         from alembic.config import Config

+         from alembic import command

+         alembic_cfg = Config(alembic_ini)

+         command.stamp(alembic_cfg, "head")

+ 

+         # Functions are not covered by models.py, and no migrations are run

+         # by command.stamp() above.  Create functions explicitly:

+         builds_logic.BuildsLogic.init_db()

+ 

+     option_list = (

+         Option("--alembic",

+                "-f",

+                dest="alembic_ini",

+                help="Path to the alembic configuration file (alembic.ini)",

+                required=True),

+     )

@@ -0,0 +1,19 @@ 

+ import os

+ import flask

+ from flask_script import Command

+ 

+ 

+ class CreateSqliteFileCommand(Command):

+ 

+     """

+     Create the sqlite DB file (not the tables).

+     Used for alembic, "create_db" does this automatically.

+     """

+ 

+     def run(self):

+         if flask.current_app.config["SQLALCHEMY_DATABASE_URI"].startswith("sqlite"):

+             # strip sqlite:///

+             datadir_name = os.path.dirname(

+                 flask.current_app.config["SQLALCHEMY_DATABASE_URI"][10:])

+             if not os.path.exists(datadir_name):

+                 os.makedirs(datadir_name)

@@ -0,0 +1,23 @@ 

+ from flask_script import Command, Option

+ from coprs.logic import coprs_logic

+ 

+ 

+ class DisplayChrootsCommand(Command):

+ 

+     "Displays current mock chroots"

+ 

+     def run(self, active_only):

+         for ch in coprs_logic.MockChrootsLogic.get_multiple(

+                 active_only=active_only).all():

+ 

+             print(ch.name)

+ 

+     option_list = (

+         Option("--active-only",

+                "-a",

+                dest="active_only",

+                help="Display only active chroots",

+                required=False,

+                action="store_true",

+                default=False),

+     )

@@ -0,0 +1,18 @@ 

+ from coprs import exceptions

+ from coprs import db

+ from coprs.logic import coprs_logic

+ from commands.create_chroot import ChrootCommand

+ 

+ class DropChrootCommand(ChrootCommand):

+ 

+     "Activates or deactivates a chroot"

+ 

+     def run(self, chroot_names):

+         for chroot_name in chroot_names:

+             try:

+                 coprs_logic.MockChrootsLogic.delete_by_name(chroot_name)

+                 db.session.commit()

+             except exceptions.MalformedArgumentException:

+                 self.print_invalid_format(chroot_name)

+             except exceptions.NotFoundException:

+                 self.print_doesnt_exist(chroot_name)

@@ -0,0 +1,12 @@ 

+ from flask_script import Command

+ from coprs import db

+ 

+ 

+ class DropDBCommand(Command):

+ 

+     """

+     Delete DB

+     """

+ 

+     def run(self):

+         db.drop_all()

@@ -0,0 +1,18 @@ 

+ from coprs import models

+ from coprs.logic import users_logic

+ from flask_script import Command, Option

+ 

+ 

+ class DumpUserCommand(Command):

+ 

+     def run(self, username):

+         user = models.User.query.filter(models.User.username == username).first()

+         if not user:

+             print("There is no user named {0}.".format(username))

+             return 1

+         dumper = users_logic.UserDataDumper(user)

+         print(dumper.dumps(pretty=True))

+ 

+     option_list = (

+         Option("username"),

+     )

@@ -0,0 +1,23 @@ 

+ import sqlalchemy

+ from flask_script import Command, Option

+ from coprs import db

+ from coprs.logic import builds_logic

+ 

+ 

+ class FailBuildCommand(Command):

+ 

+     """

+     Marks build as failed on all its non-finished chroots

+     """

+ 

+     option_list = [Option("build_id")]

+ 

+     def run(self, build_id, **kwargs):

+         try:

+             builds_logic.BuildsLogic.mark_as_failed(build_id)

+             print("Marking non-finished chroots of build {} as failed".format(build_id))

+             db.session.commit()

+ 

+         except (sqlalchemy.exc.DataError, sqlalchemy.orm.exc.NoResultFound) as e:

+             print("Error: No such build {}".format(build_id))

+             return 1

@@ -0,0 +1,9 @@ 

+ from flask_script import Command

+ from coprs import models

+ 

+ 

+ class GetAdminsCommand(Command):

+ 

+     def run(self, **kwargs):

+         for u in models.User.query.filter(models.User.admin == True).all():

+             print(u.username)

@@ -0,0 +1,74 @@ 

+ import datetime

+ from flask_script import Command, Option

+ from coprs import db

+ from coprs.logic import coprs_logic

+ from coprs.mail import send_mail, OutdatedChrootMessage

+ 

+ 

+ class NotifyOutdatedChrootsCommand(Command):

+     """

+     Notify all admins of projects with builds in outdated chroots about upcoming deletion.

+     """

+     option_list = [

+         Option("--dry-run", action="store_true",

+                help="Do not actually notify the people, but rather print information on stdout"),

+         Option("-e", "--email", action="append", dest="email_filter",

+                help="Notify only "),

+         Option("-a", "--all", action="store_true",

+                help="Notify all (even the recently notified) relevant people"),

+     ]

+ 

+     def run(self, dry_run, email_filter, all):

+         self.dry_run = dry_run

+         self.email_filter = email_filter

+         self.all = all

+ 

+         outdated = coprs_logic.CoprChrootsLogic.filter_outdated(coprs_logic.CoprChrootsLogic.get_multiple())

+         for user, chroots in self.get_user_chroots_map(outdated).items():

+             chroots = self.filter_chroots([chroot for chroot in chroots])

+             self.notify(user, chroots)

+             self.store_notify_timesamp(chroots)

+ 

+     def get_user_chroots_map(self, chroots):

+         user_chroot_map = {}

+         for chroot in chroots:

+             for admin in coprs_logic.CoprPermissionsLogic.get_admins_for_copr(chroot.copr):

+                 if self.email_filter and admin.mail not in self.email_filter:

+                     continue

+                 if admin not in user_chroot_map:

+                     user_chroot_map[admin] = []

+                 user_chroot_map[admin].append(chroot)

+         return user_chroot_map

+ 

+     def filter_chroots(self, chroots):

+         if self.all:

+             return chroots

+ 

+         filtered = []

+         for chroot in chroots:

+             if not chroot.delete_notify:

+                 filtered.append(chroot)

+                 continue

+ 

+             now = datetime.datetime.now()

+             if (now - chroot.delete_notify).days >= 14:

+                 filtered.append(chroot)

+ 

+         return filtered

+ 

+     def notify(self, user, chroots):

+         if self.dry_run:

+             about = ["{0} ({1})".format(chroot.copr.full_name, chroot.name) for chroot in chroots]

+             print("Notify {} about {}".format(user.mail, about))

+         else:

+             msg = OutdatedChrootMessage(chroots)

+             send_mail(user.mail, msg)

+ 

+     def store_notify_timesamp(self, chroots):

+         if self.dry_run:

+             return

+         for chroot in chroots:

+             chroot.delete_after_notify = datetime.datetime.now()

+         db.session.commit()

+ 

+ 

@@ -0,0 +1,79 @@ 

+ from flask_script import Command, Option

+ from copr_common.enums import StatusEnum

+ from coprs import db

+ from coprs import models

+ from coprs.logic import coprs_logic, actions_logic, builds_logic

+ 

+ 

+ class RawhideToReleaseCommand(Command):

+ 

+     option_list = (

+         Option("rawhide_chroot", help="Rawhide chroot name, e.g. fedora-rawhide-x86_64."),

+         Option("dest_chroot", help="Destination chroot, e.g. fedora-24-x86_64."),

+     )

+ 

+     def run(self, rawhide_chroot, dest_chroot):

+         mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(dest_chroot).first()

+         if not mock_chroot:

+             print("Given chroot does not exist. Please run:")

+             print("    sudo python3 manage.py create_chroot {}".format(dest_chroot))

+             return

+ 

+         mock_rawhide_chroot = coprs_logic.MockChrootsLogic.get_from_name(rawhide_chroot).first()

+         if not mock_rawhide_chroot:

+             print("Given rawhide chroot does not exist. Didnt you mistyped?:")

+             print("    {}".format(rawhide_chroot))

+             return

+ 

+         for copr in coprs_logic.CoprsLogic.get_all():

+             if not self.has_rawhide(copr) or not copr.follow_fedora_branching:

+                 continue

+ 

+             self.turn_on_the_chroot_for_copr(copr, rawhide_chroot, mock_chroot)

+ 

+             data = {"projectname": copr.name,

+                     "ownername": copr.owner_name,

+                     "rawhide_chroot": rawhide_chroot,

+                     "dest_chroot": dest_chroot,

+                     "builds": []}

+ 

+             for build in builds_logic.BuildsLogic.get_multiple_by_copr(copr):

+                 # rbc means rawhide_build_chroot (we needed short variable)

+                 rbc = builds_logic.BuildChrootsLogic.get_by_build_id_and_name(build.id, rawhide_chroot).first()

+                 dbc = builds_logic.BuildChrootsLogic.get_by_build_id_and_name(build.id, dest_chroot).first()

+ 

+                 if not rbc or rbc.status != StatusEnum("succeeded"):

+                     continue

+ 

+                 data["builds"].append(rbc.result_dir)

+ 

+                 if rbc and not dbc:

+                     dest_build_chroot = models.BuildChroot(**rbc.to_dict())

+                     dest_build_chroot.mock_chroot_id = mock_chroot.id

+                     dest_build_chroot.mock_chroot = mock_chroot

+                     dest_build_chroot.status = StatusEnum("forked")

+                     db.session.add(dest_build_chroot)

+ 

+             if len(data["builds"]):

+                 actions_logic.ActionsLogic.send_rawhide_to_release(data)

+ 

+         db.session.commit()

+ 

+     def turn_on_the_chroot_for_copr(self, copr, rawhide_name, mock_chroot):

+         rawhide_chroot = coprs_logic.CoprChrootsLogic.get_by_name_safe(copr, rawhide_name)

+         dest_chroot = coprs_logic.CoprChrootsLogic.get_by_name_safe(copr, mock_chroot.name)

+ 

+         if not rawhide_chroot or dest_chroot:

+             return

+ 

+         create_kwargs = {

+             "buildroot_pkgs": rawhide_chroot.buildroot_pkgs,

+             "comps": rawhide_chroot.comps,

+             "comps_name": rawhide_chroot.comps_name,

+         }

+         coprs_logic.CoprChrootsLogic.create_chroot(copr.user, copr, mock_chroot, **create_kwargs)

+ 

+     def has_rawhide(self, copr):

+         return any(filter(lambda ch: ch.os_version == "rawhide", copr.mock_chroots))

+ 

+ 

@@ -0,0 +1,37 @@ 

+ import argparse

+ import os

+ import subprocess

+ from flask_script import Command, Option

+ 

+ 

+ class TestCommand(Command):

+ 

+     def run(self, coverage, test_args):

+         os.environ["COPRS_ENVIRON_UNITTEST"] = "1"

+         if not (("COPR_CONFIG" in os.environ) and os.environ["COPR_CONFIG"]):

+             os.environ["COPR_CONFIG"] = "/etc/copr/copr_unit_test.conf"

+ 

+         if 'PYTHONPATH' in os.environ:

+             os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + ':.'

+         else:

+             os.environ['PYTHONPATH'] = '.'

+ 

+         additional_args = test_args

+ 

+         if coverage:

+             additional_args.extend([

+                 '--cov-report', 'term-missing', '--cov', 'coprs'

+             ])

+ 

+         return subprocess.call(["/usr/bin/python3", "-m", "pytest"] + additional_args)

+ 

+     option_list = (

+         Option("-a",

+                dest="test_args",

+                nargs=argparse.REMAINDER),

+         Option("--coverage",

+                dest="coverage",

+                required=False,

+                action='store_true',

+                default=False),

+     )

@@ -0,0 +1,13 @@ 

+ from flask_script import Command

+ from coprs.logic import builds_logic

+ 

+ 

+ class UpdateGraphsDataCommand(Command):

+     """

+     Generates newest graph data.

+     """

+ 

+     def run(self):

+         builds_logic.BuildsLogic.get_task_graph_data('10min')

+         builds_logic.BuildsLogic.get_small_graph_data('30min')

+         builds_logic.BuildsLogic.get_task_graph_data('24h')

@@ -0,0 +1,28 @@ 

+ from flask_script import Command

+ from flask_whooshee import Whooshee

+ from coprs import app

+ from coprs.whoosheers import CoprWhoosheer

+ from coprs.logic import coprs_logic

+ 

+ 

+ class UpdateIndexesCommand(Command):

+     """

+     recreates whoosh indexes for all projects

+     """

+ 

+     def run(self):

+         index = Whooshee.get_or_create_index(app, CoprWhoosheer)

+ 

+         writer = index.writer()

+         for copr in coprs_logic.CoprsLogic.get_all():

+             CoprWhoosheer.delete_copr(writer, copr)

+         writer.commit(optimize=True)

+ 

+         writer = index.writer()

+         writer.schema = CoprWhoosheer.schema

+         writer.commit(optimize=True)

+ 

+         writer = index.writer()

+         for copr in coprs_logic.CoprsLogic.get_all():

+             CoprWhoosheer.insert_copr(writer, copr)

+         writer.commit(optimize=True)

@@ -0,0 +1,28 @@ 

+ import time

+ from flask_script import Command, Option

+ from flask_whooshee import Whooshee

+ from coprs import db

+ from coprs import app

+ from coprs import models

+ from coprs.whoosheers import CoprWhoosheer

+ 

+ 

+ class UpdateIndexesQuickCommand(Command):

+     """

+     Recreates whoosh indexes for projects for which

+     indexed data were updated in last n minutes.

+     Doesn't update schema.

+     """

+ 

+     option_list = [Option("minutes_passed")]

+ 

+     def run(self, minutes_passed):

+         index = Whooshee.get_or_create_index(app, CoprWhoosheer)

+ 

+         writer = index.writer()

+         query = db.session.query(models.Copr).filter(

+             models.Copr.latest_indexed_data_update >= time.time()-int(minutes_passed)*60

+         )

+         for copr in query.all():

+             CoprWhoosheer.update_copr(writer, copr)

+         writer.commit()

@@ -0,0 +1,23 @@ 

+ import time

+ from sqlalchemy import and_, or_

+ from flask_script import Command

+ from coprs import db

+ from coprs import models

+ 

+ 

+ class RemoveGraphsDataCommand(Command):

+     """

+     Removes old cached graph data that is no longer used.

+     """

+ 

+     def run(self):

+         curr_time = int(time.time())

+         models.BuildsStatistics.query.filter(or_(

+             and_(models.BuildsStatistics.time < curr_time - 91 * 86400,

+                  models.BuildsStatistics.stat_type == '24h'),

+             and_(models.BuildsStatistics.time < curr_time - 87000,

+                  models.BuildsStatistics.stat_type == '30min'),

+             and_(models.BuildsStatistics.time < curr_time - 87000,

+                  models.BuildsStatistics.stat_type == '10min')

+         )).delete()

+         db.session.commit()

@@ -1,619 +1,50 @@ 

  #!/usr/bin/python3

  

- import argparse

- import os

- import subprocess

- import sqlalchemy

- import datetime

- import time

  

- import flask

- from flask_script import Manager, Command, Option, Group

- from flask_whooshee import Whooshee

- 

- from copr_common.enums import StatusEnum

+ import importlib

+ from flask_script import Manager

  from coprs import app

- from coprs import db

- from coprs import exceptions

- from coprs import models

- from coprs.mail import send_mail, OutdatedChrootMessage

- from coprs.logic import coprs_logic, packages_logic, actions_logic, builds_logic, users_logic

- from coprs.views.misc import create_user_wrapper

- from coprs.whoosheers import CoprWhoosheer

- from sqlalchemy import and_, or_

- from coprs.helpers import chroot_to_branch, PermissionEnum

- 

- 

- class TestCommand(Command):

- 

-     def run(self, coverage, test_args):

-         os.environ["COPRS_ENVIRON_UNITTEST"] = "1"

-         if not (("COPR_CONFIG" in os.environ) and os.environ["COPR_CONFIG"]):

-             os.environ["COPR_CONFIG"] = "/etc/copr/copr_unit_test.conf"

- 

-         if 'PYTHONPATH' in os.environ:

-             os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + ':.'

-         else:

-             os.environ['PYTHONPATH'] = '.'

- 

-         additional_args = test_args

- 

-         if coverage:

-             additional_args.extend([

-                 '--cov-report', 'term-missing', '--cov', 'coprs'

-             ])

- 

-         return subprocess.call(["/usr/bin/python3", "-m", "pytest"] + additional_args)

- 

-     option_list = (

-         Option("-a",

-                dest="test_args",

-                nargs=argparse.REMAINDER),

-         Option("--coverage",

-                dest="coverage",

-                required=False,

-                action='store_true',

-                default=False),

-     )

- 

- 

- class CreateSqliteFileCommand(Command):

- 

-     """

-     Create the sqlite DB file (not the tables).

-     Used for alembic, "create_db" does this automatically.

-     """

- 

-     def run(self):

-         if flask.current_app.config["SQLALCHEMY_DATABASE_URI"].startswith("sqlite"):

-             # strip sqlite:///

-             datadir_name = os.path.dirname(

-                 flask.current_app.config["SQLALCHEMY_DATABASE_URI"][10:])

-             if not os.path.exists(datadir_name):

-                 os.makedirs(datadir_name)

- 

- 

- class CreateDBCommand(Command):

- 

-     """

-     Create the DB schema

-     """

- 

-     def run(self, alembic_ini=None):

-         CreateSqliteFileCommand().run()

-         db.create_all()

- 

-         # load the Alembic configuration and generate the

-         # version table, "stamping" it with the most recent rev:

-         from alembic.config import Config

-         from alembic import command

-         alembic_cfg = Config(alembic_ini)

-         command.stamp(alembic_cfg, "head")

- 

-         # Functions are not covered by models.py, and no migrations are run

-         # by command.stamp() above.  Create functions explicitly:

-         builds_logic.BuildsLogic.init_db()

- 

-     option_list = (

-         Option("--alembic",

-                "-f",

-                dest="alembic_ini",

-                help="Path to the alembic configuration file (alembic.ini)",

-                required=True),

-     )

- 

- 

- class DropDBCommand(Command):

- 

-     """

-     Delete DB

-     """

- 

-     def run(self):

-         db.drop_all()

- 

- 

- class ChrootCommand(Command):

- 

-     def print_invalid_format(self, chroot_name):

-         print(

-             "{0} - invalid chroot format, must be '{release}-{version}-{arch}'."

-             .format(chroot_name))

- 

-     def print_already_exists(self, chroot_name):

-         print("{0} - already exists.".format(chroot_name))

- 

-     def print_doesnt_exist(self, chroot_name):

-         print("{0} - chroot doesn\"t exist.".format(chroot_name))

- 

-     option_list = (

-         Option("chroot_names",

-                help="Chroot name, e.g. fedora-18-x86_64.",

-                nargs="+"),

-     )

- 

- 

- class CreateChrootCommand(ChrootCommand):

- 

-     "Creates a mock chroot in DB"

- 

-     def __init__(self):

-         self.option_list += Option(

-                "--dist-git-branch",

-                "-b",

-                dest="branch",

-                help="Branch name for this set of new chroots"),

- 

-     def run(self, chroot_names, branch=None):

-         for chroot_name in chroot_names:

-             if not branch:

-                 branch = chroot_to_branch(chroot_name)

-             branch_object = coprs_logic.BranchesLogic.get_or_create(branch)

-             try:

-                 chroot = coprs_logic.MockChrootsLogic.add(chroot_name)

-                 chroot.distgit_branch = branch_object

-                 db.session.commit()

-             except exceptions.MalformedArgumentException:

-                 self.print_invalid_format(chroot_name)

-             except exceptions.DuplicateException:

-                 self.print_already_exists(chroot_name)

- 

- 

- class RawhideToReleaseCommand(Command):

- 

-     option_list = (

-         Option("rawhide_chroot", help="Rawhide chroot name, e.g. fedora-rawhide-x86_64."),

-         Option("dest_chroot", help="Destination chroot, e.g. fedora-24-x86_64."),

-     )

- 

-     def run(self, rawhide_chroot, dest_chroot):

-         mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(dest_chroot).first()

-         if not mock_chroot:

-             print("Given chroot does not exist. Please run:")

-             print("    sudo python3 manage.py create_chroot {}".format(dest_chroot))

-             return

- 

-         mock_rawhide_chroot = coprs_logic.MockChrootsLogic.get_from_name(rawhide_chroot).first()

-         if not mock_rawhide_chroot:

-             print("Given rawhide chroot does not exist. Didnt you mistyped?:")

-             print("    {}".format(rawhide_chroot))

-             return

- 

-         for copr in coprs_logic.CoprsLogic.get_all():

-             if not self.has_rawhide(copr) or not copr.follow_fedora_branching:

-                 continue

- 

-             self.turn_on_the_chroot_for_copr(copr, rawhide_chroot, mock_chroot)

- 

-             data = {"projectname": copr.name,

-                     "ownername": copr.owner_name,

-                     "rawhide_chroot": rawhide_chroot,

-                     "dest_chroot": dest_chroot,

-                     "builds": []}

- 

-             for build in builds_logic.BuildsLogic.get_multiple_by_copr(copr):

-                     # rbc means rawhide_build_chroot (we needed short variable)

-                     rbc = builds_logic.BuildChrootsLogic.get_by_build_id_and_name(build.id, rawhide_chroot).first()

-                     dbc = builds_logic.BuildChrootsLogic.get_by_build_id_and_name(build.id, dest_chroot).first()

- 

-                     if not rbc or rbc.status != StatusEnum("succeeded"):

-                         continue

- 

-                     data["builds"].append(rbc.result_dir)

- 

-                     if rbc and not dbc:

-                         dest_build_chroot = models.BuildChroot(**rbc.to_dict())

-                         dest_build_chroot.mock_chroot_id = mock_chroot.id

-                         dest_build_chroot.mock_chroot = mock_chroot

-                         dest_build_chroot.status = StatusEnum("forked")

-                         db.session.add(dest_build_chroot)

- 

-             if len(data["builds"]):

-                 actions_logic.ActionsLogic.send_rawhide_to_release(data)

- 

-         db.session.commit()

- 

-     def turn_on_the_chroot_for_copr(self, copr, rawhide_name, mock_chroot):

-         rawhide_chroot = coprs_logic.CoprChrootsLogic.get_by_name_safe(copr, rawhide_name)

-         dest_chroot = coprs_logic.CoprChrootsLogic.get_by_name_safe(copr, mock_chroot.name)

- 

-         if not rawhide_chroot or dest_chroot:

-             return

- 

-         create_kwargs = {

-             "buildroot_pkgs": rawhide_chroot.buildroot_pkgs,

-             "comps": rawhide_chroot.comps,

-             "comps_name": rawhide_chroot.comps_name,

-         }

-         coprs_logic.CoprChrootsLogic.create_chroot(copr.user, copr, mock_chroot, **create_kwargs)

- 

-     def has_rawhide(self, copr):

-         return any(filter(lambda ch: ch.os_version == "rawhide", copr.mock_chroots))

- 

- 

- class BackendRawhideToReleaseCommand(RawhideToReleaseCommand):

- 

-     "Copy backend data of the latest successful rawhide builds into a new chroot"

- 

-     def run(self, rawhide_chroot, dest_chroot):

-         for copr in coprs_logic.CoprsLogic.get_all():

-             if not self.has_rawhide(copr):

-                 continue

- 

-             data = {"copr": copr.name,

-                     "user": copr.owner_name,

-                     "rawhide_chroot": rawhide_chroot,

-                     "dest_chroot": dest_chroot,

-                     "builds": []}

- 

-             for package in packages_logic.PackagesLogic.get_all(copr.id):

-                 last_build = package.last_build(successful=True)

-                 if last_build:

-                     data["builds"].append(last_build.result_dir)

- 

-             if len(data["builds"]):

-                 actions_logic.ActionsLogic.send_rawhide_to_release(data)

-                 print("Created copy action from {}/{} to {}/{}"

-                       .format(copr.full_name, rawhide_chroot, copr.full_name, dest_chroot))

- 

-         db.session.commit()

- 

- 

- class AlterChrootCommand(ChrootCommand):

- 

-     "Activates or deactivates a chroot"

  

-     def run(self, chroot_names, action):

-         activate = (action == "activate")

-         for chroot_name in chroot_names:

-             try:

-                 mock_chroot = coprs_logic.MockChrootsLogic.edit_by_name(

-                     chroot_name, activate)

  

-                 if action != "eol":

-                     continue

+ commands = {

+     # General commands

+     "test": "TestCommand",

  

-                 for copr_chroot in mock_chroot.copr_chroots:

-                     delete_after_days = app.config["DELETE_EOL_CHROOTS_AFTER"] + 1

-                     delete_after_timestamp = datetime.datetime.now() + datetime.timedelta(delete_after_days)

-                     # Workarounding an auth here

-                     coprs_logic.CoprChrootsLogic.update_chroot(copr_chroot.copr.user, copr_chroot,

-                                                                delete_after=delete_after_timestamp)

-                 db.session.commit()

-             except exceptions.MalformedArgumentException:

-                 self.print_invalid_format(chroot_name)

-             except exceptions.NotFoundException:

-                 self.print_doesnt_exist(chroot_name)

+     # Database commands

+     "create_sqlite_file": "CreateSqliteFileCommand",

+     "create_db": "CreateDBCommand",

+     "drop_db": "DropDBCommand",

  

-     option_list = ChrootCommand.option_list + (

-         Option("--action",

-                "-a",

-                dest="action",

-                help="Action to take - currently activate or deactivate",

-                choices=["activate", "deactivate", "eol"],

-                required=True),

-     )

+     # Chroot commands

+     "create_chroot": "CreateChrootCommand",

+     "alter_chroot": "AlterChrootCommand",

+     "display_chroots": "DisplayChrootsCommand",

+     "drop_chroot": "DropChrootCommand",

  

+     # User commands

+     "alter_user": "AlterUserCommand",

+     "add_user": "AddUserCommand",

+     "dump_user": "DumpUserCommand",

  

- class DropChrootCommand(ChrootCommand):

- 

-     "Activates or deactivates a chroot"

- 

-     def run(self, chroot_names):

-         for chroot_name in chroot_names:

-             try:

-                 coprs_logic.MockChrootsLogic.delete_by_name(chroot_name)

-                 db.session.commit()

-             except exceptions.MalformedArgumentException:

-                 self.print_invalid_format(chroot_name)

-             except exceptions.NotFoundException:

-                 self.print_doesnt_exist(chroot_name)

- 

- 

- class DisplayChrootsCommand(Command):

- 

-     "Displays current mock chroots"

- 

-     def run(self, active_only):

-         for ch in coprs_logic.MockChrootsLogic.get_multiple(

-                 active_only=active_only).all():

- 

-             print(ch.name)

- 

-     option_list = (

-         Option("--active-only",

-                "-a",

-                dest="active_only",

-                help="Display only active chroots",

-                required=False,

-                action="store_true",

-                default=False),

-     )

- 

- 

- class AddUserCommand(Command):

- 

-     """

-     You should not use regularly as that user will not be related to FAS account.

-     This should be used only for testing or adding special accounts e.g. proxy user.

-     """

- 

-     def run(self, name, mail, **kwargs):

-         user = models.User.query.filter(models.User.username == name).first()

-         if user:

-             print("User named {0} already exists.".format(name))

-             return

- 

-         user = create_user_wrapper(name, mail)

-         if kwargs["api_token"]:

-             user.api_token = kwargs["api_token"]

-         if kwargs["api_login"]:

-             user.api_token = kwargs["api_login"]

- 

-         db.session.add(user)

-         db.session.commit()

- 

-     option_list = (

-         Option("name"),

-         Option("mail"),

-         Option("--api_token", default=None, required=False),

-         Option("--api_login", default=None, required=False),

-     )

- 

- 

- class DumpUserCommand(Command):

- 

-     def run(self, username):

-         user = models.User.query.filter(models.User.username == username).first()

-         if not user:

-             print("There is no user named {0}.".format(username))

-             return 1

-         dumper = users_logic.UserDataDumper(user)

-         print(dumper.dumps(pretty=True))

- 

-     option_list = (

-         Option("username"),

-     )

- 

- 

- class AlterUserCommand(Command):

- 

-     def run(self, name, **kwargs):

-         user = models.User.query.filter(

-             models.User.username == name).first()

-         if not user:

-             print("No user named {0}.".format(name))

-             return

- 

-         if kwargs["admin"]:

-             user.admin = True

-         if kwargs["no_admin"]:

-             user.admin = False

-         if kwargs["proven"]:

-             user.proven = True

-         if kwargs["no_proven"]:

-             user.proven = False

-         if kwargs["proxy"]:

-             user.proxy = True

-         if kwargs["no_proxy"]:

-             user.proxy = False

- 

-         db.session.add(user)

-         db.session.commit()

- 

-     option_list = (

-         Option("name"),

-         Group(

-             Option("--admin",

-                    action="store_true"),

-             Option("--no-admin",

-                    action="store_true"),

-             exclusive=True

-         ),

-         Group(

-             Option("--proven",

-                    action="store_true"),

-             Option("--no-proven",

-                    action="store_true"),

-             exclusive=True

-         ),

-         Group(

-             Option("--proxy",

-                    action="store_true"),

-             Option("--no-proxy",

-                    action="store_true"),

-             exclusive=True

-         )

-     )

- 

- 

- class FailBuildCommand(Command):

- 

-     """

-     Marks build as failed on all its non-finished chroots

-     """

- 

-     option_list = [Option("build_id")]

- 

-     def run(self, build_id, **kwargs):

-         try:

-             builds_logic.BuildsLogic.mark_as_failed(build_id)

-             print("Marking non-finished chroots of build {} as failed".format(build_id))

-             db.session.commit()

- 

-         except (sqlalchemy.exc.DataError, sqlalchemy.orm.exc.NoResultFound) as e:

-             print("Error: No such build {}".format(build_id))

-             return 1

- 

- 

- class UpdateIndexesCommand(Command):

-     """

-     recreates whoosh indexes for all projects

-     """

- 

-     def run(self):

-         index = Whooshee.get_or_create_index(app, CoprWhoosheer)

- 

-         writer = index.writer()

-         for copr in coprs_logic.CoprsLogic.get_all():

-             CoprWhoosheer.delete_copr(writer, copr)

-         writer.commit(optimize=True)

- 

-         writer = index.writer()

-         writer.schema = CoprWhoosheer.schema

-         writer.commit(optimize=True)

- 

-         writer = index.writer()

-         for copr in coprs_logic.CoprsLogic.get_all():

-             CoprWhoosheer.insert_copr(writer, copr)

-         writer.commit(optimize=True)

- 

- 

- class UpdateIndexesQuickCommand(Command):

-     """

-     Recreates whoosh indexes for projects for which

-     indexed data were updated in last n minutes.

-     Doesn't update schema.

-     """

- 

-     option_list = [Option("minutes_passed")]

- 

-     def run(self, minutes_passed):

-         index = Whooshee.get_or_create_index(app, CoprWhoosheer)

- 

-         writer = index.writer()

-         query = db.session.query(models.Copr).filter(

-             models.Copr.latest_indexed_data_update >= time.time()-int(minutes_passed)*60

-         )

-         for copr in query.all():

-             CoprWhoosheer.update_copr(writer, copr)

-         writer.commit()

- 

- 

- class UpdateGraphsDataCommand(Command):

-     """

-     Generates newest graph data.

-     """

- 

-     def run(self):

-         builds_logic.BuildsLogic.get_task_graph_data('10min')

-         builds_logic.BuildsLogic.get_small_graph_data('30min')

-         builds_logic.BuildsLogic.get_task_graph_data('24h')

- 

- 

- class RemoveGraphsDataCommand(Command):

-     """

-     Removes old cached graph data that is no longer used.

-     """

- 

-     def run(self):

-         curr_time = int(time.time())

-         models.BuildsStatistics.query.filter(or_(

-             and_(models.BuildsStatistics.time < curr_time - 91 * 86400,

-                  models.BuildsStatistics.stat_type == '24h'),

-             and_(models.BuildsStatistics.time < curr_time - 87000,

-                  models.BuildsStatistics.stat_type == '30min'),

-             and_(models.BuildsStatistics.time < curr_time - 87000,

-                  models.BuildsStatistics.stat_type == '10min')

-         )).delete()

-         db.session.commit()

- 

- 

- class NotifyOutdatedChrootsCommand(Command):

-     """

-     Notify all admins of projects with builds in outdated chroots about upcoming deletion.

-     """

-     option_list = [

-         Option("--dry-run", action="store_true",

-                help="Do not actually notify the people, but rather print information on stdout"),

-         Option("-e", "--email", action="append", dest="email_filter",

-                help="Notify only "),

-         Option("-a", "--all", action="store_true",

-                help="Notify all (even the recently notified) relevant people"),

-     ]

- 

-     def run(self, dry_run, email_filter, all):

-         self.dry_run = dry_run

-         self.email_filter = email_filter

-         self.all = all

- 

-         outdated = coprs_logic.CoprChrootsLogic.filter_outdated(coprs_logic.CoprChrootsLogic.get_multiple())

-         for user, chroots in self.get_user_chroots_map(outdated).items():

-             chroots = self.filter_chroots([chroot for chroot in chroots])

-             self.notify(user, chroots)

-             self.store_notify_timesamp(chroots)

- 

-     def get_user_chroots_map(self, chroots):

-         user_chroot_map = {}

-         for chroot in chroots:

-             for admin in coprs_logic.CoprPermissionsLogic.get_admins_for_copr(chroot.copr):

-                 if self.email_filter and admin.mail not in self.email_filter:

-                     continue

-                 if admin not in user_chroot_map:

-                     user_chroot_map[admin] = []

-                 user_chroot_map[admin].append(chroot)

-         return user_chroot_map

- 

-     def filter_chroots(self, chroots):

-         if self.all:

-             return chroots

- 

-         filtered = []

-         for chroot in chroots:

-             if not chroot.delete_notify:

-                 filtered.append(chroot)

-                 continue

- 

-             now = datetime.datetime.now()

-             if (now - chroot.delete_notify).days >= 14:

-                 filtered.append(chroot)

- 

-         return filtered

- 

-     def notify(self, user, chroots):

-         if self.dry_run:

-             about = ["{0} ({1})".format(chroot.copr.full_name, chroot.name) for chroot in chroots]

-             print("Notify {} about {}".format(user.mail, about))

-         else:

-             msg = OutdatedChrootMessage(chroots)

-             send_mail(user.mail, msg)

- 

-     def store_notify_timesamp(self, chroots):

-         if self.dry_run:

-             return

-         for chroot in chroots:

-             chroot.delete_after_notify = datetime.datetime.now()

-         db.session.commit()

- 

- 

- class GetAdminsCommand(Command):

- 

-     def run(self, **kwargs):

-         for u in models.User.query.filter(models.User.admin == True).all():

-             print(u.username)

+     # Other

+     "get_admins": "GetAdminsCommand",

+     "fail_build": "FailBuildCommand",

+     "update_indexes": "UpdateIndexesCommand",

+     "update_indexes_quick": "UpdateIndexesQuickCommand",

+     "rawhide_to_release": "RawhideToReleaseCommand",

+     "backend_rawhide_to_release": "BackendRawhideToReleaseCommand",

+     "update_graphs": "UpdateGraphsDataCommand",

+     "vacuum_graphs": "RemoveGraphsDataCommand",

+     "notify_outdated_chroots": "NotifyOutdatedChrootsCommand",

+ }

  

  

  manager = Manager(app)

- manager.add_command("test", TestCommand())

- manager.add_command("create_sqlite_file", CreateSqliteFileCommand())

- manager.add_command("create_db", CreateDBCommand())

- manager.add_command("drop_db", DropDBCommand())

- manager.add_command("create_chroot", CreateChrootCommand())

- manager.add_command("alter_chroot", AlterChrootCommand())

- manager.add_command("display_chroots", DisplayChrootsCommand())

- manager.add_command("drop_chroot", DropChrootCommand())

- manager.add_command("alter_user", AlterUserCommand())

- manager.add_command("add_user", AddUserCommand())

- manager.add_command("dump_user", DumpUserCommand())

- manager.add_command("fail_build", FailBuildCommand())

- manager.add_command("update_indexes", UpdateIndexesCommand())

- manager.add_command("update_indexes_quick", UpdateIndexesQuickCommand())

- manager.add_command("rawhide_to_release", RawhideToReleaseCommand())

- manager.add_command("backend_rawhide_to_release", BackendRawhideToReleaseCommand())

- manager.add_command("update_graphs", UpdateGraphsDataCommand())

- manager.add_command("vacuum_graphs", RemoveGraphsDataCommand())

- manager.add_command("notify_outdated_chroots", NotifyOutdatedChrootsCommand())

- manager.add_command("get_admins", GetAdminsCommand())

+ for cmdname, clsname in commands.items():

+     module = importlib.import_module("commands.{0}".format(cmdname))

+     cls = getattr(module, clsname)

+     manager.add_command(cmdname, cls())

+ 

  

  if __name__ == "__main__":

      manager.run()

IMHO the content of manage.py file is getting out of control. I am
moving all the *Command implementations to their own files to gain
better readability and orientation in the code, but also possibility
to implement helper stuff needed for the command.

Some of the commands are quite long and contain some logic.
Such logic needs to be implemented on the class level, which can
be annoying (e.g. some additional helper class needs to be added as
a nested class). Ofc it can be put on the module level and mixed among
command classes, but this solution isn't very good. Another solution
would be putting such code into coprs.logic or coprs.helpers, but
it doesn't make much sense, if its only purpose is to work within the
command.

Therefore, I am separating the commands into their own files to get
rid of any limitations of their implementation.

Alternatively, we can separate only more-complicated commands into their own files if you want.

Pull-Request has been merged by msuchy

5 years ago